package demo.java.sql.service;

import demo.java.sql.JdbcDemo;
import demo.util.CommonUtil;
import demo.util.DesSecurityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Clock;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 对如下表的部分字段进行解密校验
 * <li>1.approve_extend_info（加密卡号）
 * <li>2.approve_info （加密id_no,name）
 * <li>3.approve_info_history（加密id_no,name）
 * <li>4.call_log(只加密用户名字,update_at字段会自动更新，需要避免这个因素发生)
 * 
 * @author hanjy
 *
 */
@Service
public class DataValidService {

    private static Logger logger = LoggerFactory.getLogger(DataValidService.class);

    @Autowired
    private DataSource maDataSource;

    static Clock clock = Clock.systemDefaultZone();

    /**
     * 执行SELECT 语句，并返回结果
     * 
     * @author hanjy
     *
     */
    public static class SelectSqlTask implements Callable<ResultSet> {

        private PreparedStatement preparedStatment;
        private Object[] parameters;
        private String sql;

        public SelectSqlTask(PreparedStatement preparedStatment, String sql, Object... objects) {
            super();
            this.preparedStatment = preparedStatment;
            this.sql = sql;
            this.parameters = objects;
        }

        @Override
        public ResultSet call() throws Exception {
            if (parameters != null && parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    preparedStatment.setObject(i + 1, parameters[i]);
                }
            }
            String sqlLog = sql.replaceAll("\\?", "{}");
            long t1 = clock.millis();
            ResultSet resultSet = preparedStatment.executeQuery();
            logger.info(sqlLog, parameters);
            logger.info("执行查询耗时:{} ms", (clock.millis() - t1));
            return resultSet;
        }

    }

    /**
     * 全量的数据校验
     * <p>
     * <li>如果数据为空，返回false。
     * <li>如果数据总数不一致返回false。
     * <li>逐个比较非忽略字段，如果不一致则返回false。
     * 
     * @param srcSelect
     *            源表的查询语句
     * @param tarSelect
     *            目标表的查询语句
     * @throws InterruptedException
     */
    public void verifyAllData(String srcSelect, String tarSelect, int batchRows, int[] index)
            throws InterruptedException {
        try (Connection srcConnection = maDataSource.getConnection();
                Connection tarConnection = maDataSource.getConnection();
                PreparedStatement srcStatement = srcConnection.prepareStatement(srcSelect,
                        ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
                PreparedStatement tarPStatement = tarConnection.prepareStatement(tarSelect,
                        ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);) {
            int offset = 0;
            List<Object[]> srcList = null;
            List<Object[]> tarList = null;
            do {
                FutureTask<ResultSet> srcFutureTask = new FutureTask<>(
                        new SelectSqlTask(srcStatement, srcSelect, offset, batchRows));
                new Thread(srcFutureTask).start();

                FutureTask<ResultSet> tarFutureTask = new FutureTask<>(
                        new SelectSqlTask(tarPStatement, tarSelect, offset, batchRows));
                new Thread(tarFutureTask).start();

                while (!srcFutureTask.isDone() || !tarFutureTask.isDone()) {
                    Thread.sleep(30);
                }
                try (ResultSet srcRS = srcFutureTask.get(); ResultSet tarRS = tarFutureTask.get();) {
                    srcList = JdbcDemo.buildResultSet(srcRS);
                    DesSecurityUtil.encryptOrDecrypt(srcList, index, false);
                    tarList = JdbcDemo.buildResultSet(tarRS);
                    boolean equal = CommonUtil.equals(srcList, tarList);
                    if (!equal) {
                        logger.error("数据校验失败！");
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                offset += batchRows;
            } while (srcList != null && srcList.size() == batchRows);

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        }
    }

}
